package com.tanger.common.jpa;

import com.fasterxml.jackson.databind.JsonNode;
import com.tanger.common.page.QueryOP;
import com.tanger.common.utils.JSON;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.util.CollectionUtils;

import javax.persistence.criteria.*;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Description JPA条件构造器
 * @Author Hchenbin
 * @Date 2024-01-09 09:39
 * @Version 1.0
 */
public class ExampleBuilder {

    /**
     * @Description 构造查询条件
     * @param params 构造参数
     * @return
     */
    public static Specification builder(QueryParams params) {
        List<QueryParams.MatcherReq> cts = params.getFields();
        if (CollectionUtils.isEmpty(cts)) return null;

        Map<String, List<QueryParams.MatcherReq>> group = cts.stream().collect(Collectors.groupingBy(QueryParams.MatcherReq::getType));
        return (Specification) (root, query, cb) -> {
            //增加筛选条件
            List<Predicate> predicateList= new ArrayList<>();
            Iterator<Map.Entry<String, List<QueryParams.MatcherReq>>> it = group.entrySet().iterator();
            while (it.hasNext()) {

                Predicate predicate = cb.conjunction();
                Map.Entry<String, List<QueryParams.MatcherReq>> entry = it.next();

                List<QueryParams.MatcherReq> matchs = entry.getValue();
                if (CollectionUtils.isEmpty(matchs)) {
                    continue;
                }
                predicateList.add(match(entry.getKey(), matchs, predicate, root, cb));
            }
            query.where(cb.and(predicateList.toArray(new Predicate[predicateList.size()])));
            return query.getRestriction();
        };
    }

    /**
     * @Description 匹配构造器
     * @param exGenre 表达式
     * @param matchs 构造参数
     * @param predicate
     * @param root
     * @param cb
     * @return
     */
    private static Predicate match(String exGenre, List<QueryParams.MatcherReq> matchs,
                                   Predicate predicate, Root root, CriteriaBuilder cb){
        if (exGenre.equals(QueryOP.EQUAL.name())) {                 //等于
            return expression_equal(matchs, predicate, root, cb);
        } else if (exGenre.equals(QueryOP.NOT_EQUAL.name())) {      //不等于
            return expression_not_equal(matchs, predicate, root, cb);
        } else if (exGenre.equals(QueryOP.BETWEEN.name())) {        //两者之间
            return expression_between(matchs, predicate, root, cb);
        } else if (exGenre.equals(QueryOP.LIKE.name())) {           //忽略大小写模糊查询
            return expression_like(matchs, predicate, root, cb);
        } else if (exGenre.equals(QueryOP.IN.name())) {             //在列表内
            return expression_in(matchs ,predicate, root);
        } else if (exGenre.equals(QueryOP.GREAT.name())) {          //大于
            return expression_gt(matchs, predicate, root, cb);
        } else if (exGenre.equals(QueryOP.GREAT_EQUAL.name())) {    //大于等于
            return expression_ge(matchs, predicate, root, cb);
        } else if (exGenre.equals(QueryOP.LESS.name())) {           //小于
            return expression_lt(matchs, predicate, root, cb);
        } else if (exGenre.equals(QueryOP.LESS_EQUAL.name())) {     //小于等于
            return expression_le(matchs, predicate, root, cb);
        }
        return predicate;
    }

    /**
     * @Description JPA EQUAL表达式构造器
     * @param groupMatchs
     * @param predicate
     * @param root
     * @param cb
     */
    private static Predicate expression_equal(
            List<QueryParams.MatcherReq> groupMatchs, Predicate predicate, Root root, CriteriaBuilder cb){
        groupMatchs.stream()
                .forEach(v -> {
                    predicate.getExpressions().add(cb.equal(root.get(v.getName()), v.getValue()));
                });
        return predicate;
    }

    /**
     * @Description JPA NE(不等于)表达式构造器
     * @param groupMatchs
     * @param predicate
     * @param root
     * @param cb
     */
    private static Predicate expression_not_equal(
            List<QueryParams.MatcherReq> groupMatchs, Predicate predicate, Root root, CriteriaBuilder cb){
        groupMatchs.stream()
                .forEach(v -> {
                    predicate.getExpressions().add(cb.notEqual(root.get(v.getName()), v.getValue()));
                });
        return predicate;
    }

    /**
     * @Description JPA BETWEEN表达式构造器
     * @param groupMatchs
     * @param predicate
     * @param root
     * @param cb
     */
    private static Predicate expression_between(
            List<QueryParams.MatcherReq> groupMatchs, Predicate predicate, Root root, CriteriaBuilder cb){
        groupMatchs.stream()
                .forEach(v -> {
                    JsonNode value = JSON.parse(JSON.stringify(v.getValue()), JsonNode.class);
                    predicate.getExpressions()
                            .add(cb.between(root.get(v.getName()),
                                    value.get("start").asLong(),
                                    value.get("end").asLong()));
                });
        return predicate;
    }

    /**
     * @Description JPA LIKE（含多属性）表达式构造器,模糊查询多个字段时 name","号分割
     * @param groupMatchs
     * @param predicate
     * @param root
     * @param cb
     */
    private static Predicate expression_like(
            List<QueryParams.MatcherReq> groupMatchs, Predicate predicate, Root root, CriteriaBuilder cb){
        groupMatchs.stream()
                .forEach(v -> {
                    //验证是否包含多个属性
                    if (!v.getName().contains(",")) {
                        predicate.getExpressions()
                                .add(cb.like(// 忽略大小写
                                        cb.lower(root.get(v.getName())),
                                        "%" + v.getValue().toString().toLowerCase() + "%"));
                    } else {
                        String[] peopertys = v.getName().split(",");
                        List<Predicate> pcs = new ArrayList<>(peopertys.length);
                        for (int i = 0; i < peopertys.length; i++) {
                            Expression<String> property = root.get(peopertys[i]);
                            Predicate pc = cb.like(cb.lower(property), "%" + v.getValue().toString().toLowerCase() + "%");
                            pcs.add(pc);
                        }
                        Predicate orConstruct = cb.or(pcs.toArray(new Predicate[pcs.size()]));
                        predicate.getExpressions().add(orConstruct);
                    }
                });
        return predicate;
    }

    /**
     * @Description JPA IN表达式构造器
     * @param groupMatchs
     * @param predicate
     * @param root
     */
    private static Predicate expression_in(
            List<QueryParams.MatcherReq> groupMatchs, Predicate predicate, Root root){
        groupMatchs.stream()
                .forEach(v -> {
                    predicate.getExpressions().add(root.get(v.getName()).in((List) v.getValue()));
                });
        return predicate;
    }

    /**
     * @Description JPA GE(大于等于)表达式构造器
     * @param groupMatchs
     * @param predicate
     * @param root
     * @param cb
     */
    private static Predicate expression_ge(
            List<QueryParams.MatcherReq> groupMatchs, Predicate predicate, Root root, CriteriaBuilder cb){
        groupMatchs.stream()
                .forEach(v -> {
                    predicate.getExpressions()
                            .add(cb.greaterThanOrEqualTo(root.get(v.getName()).as(Long.class), (Long)v.getValue()));
                });
        return predicate;
    }

    /**
     * @Description JPA GT(大于)表达式构造器
     * @param groupMatchs
     * @param predicate
     * @param root
     * @param cb
     */
    private static Predicate expression_gt(
            List<QueryParams.MatcherReq> groupMatchs, Predicate predicate, Root root, CriteriaBuilder cb){
        groupMatchs.stream()
                .forEach(v -> {
                    predicate.getExpressions()
                            .add(cb.gt(root.get(v.getName()).as(Long.class), (Long)v.getValue()));
                });
        return predicate;
    }

    /**
     * @Description JPA LE(小于等于)表达式构造器
     * @param groupMatchs
     * @param predicate
     * @param root
     * @param cb
     */
    private static Predicate expression_le(
            List<QueryParams.MatcherReq> groupMatchs, Predicate predicate, Root root, CriteriaBuilder cb){
        groupMatchs.stream()
                .forEach(v -> {
                    predicate.getExpressions()
                            .add(cb.lessThanOrEqualTo(root.get(v.getName()).as(Long.class), (Long)v.getValue()));
                });
        return predicate;
    }

    /**
     * @Description JPA LT(小于)表达式构造器
     * @param groupMatchs
     * @param predicate
     * @param root
     * @param cb
     */
    private static Predicate expression_lt(
            List<QueryParams.MatcherReq> groupMatchs, Predicate predicate, Root root, CriteriaBuilder cb){
        groupMatchs.stream()
                .forEach(v -> {
                    predicate.getExpressions()
                            .add(cb.lt(root.get(v.getName()).as(Long.class), (Long)v.getValue()));
                });
        return predicate;
    }
}
